#ifndef VECTORCORE_H_INCLUDED
#define VECTORCORE_H_INCLUDED

#include "Constants.h"
#include "MemoryManager.h"
#include <cstring>
#include <new>

namespace CEngine {

template<class T>
Vector<T>::Vector(void)
    :   array(NULL),
        size(0),
        capacity(0)
{}

template<class T>
Vector<T>::Vector(const U64 n)
    :   array(NULL),
        size(n),
        capacity(n)
{
    array = (T*)MemoryManager::alloc(n * sizeof(T));

    for (U64 i = 0; i < size; ++i) {
        new(&array[i]) T;
    }
}

template<class T>
Vector<T>::Vector(const U64 n, const T& element)
    :   array(NULL),
        size(n),
        capacity(n)
{
    array = (T*)MemoryManager::alloc(n * sizeof(T));

    for (U64 i = 0; i < size; ++i) {
        array[i] = element;
    }
}

template<class T>
Vector<T>::Vector(const Iterator<T>& first, const Iterator<T>& last)
    :   array(NULL)
{
    ASSERT(first < last);

    size = last.getPosition() - first.getPosition();

    array = (T*)MemoryManager::alloc(size * sizeof(T));

    Iterator<T> it = first;
    for (U64 i = 0; it < last; ++it) {
        array[i++] = *it;
    }
}

template<class T>
Vector<T>::Vector(const Vector& vector)
    :   array(NULL),
        size(vector.size),
        capacity(vector.capacity)
{
    array = (T*)MemoryManager::alloc(capacity * sizeof(T));

    memcpy(array, vector.array, size * sizeof(T));
}

template<class T>
Vector<T>&
Vector<T>::operator=(const Vector& vector) {
    if(&vector == this) {
        return *this;
    }

    for (U64 i = 0; i < size; ++i) {
        array[i].~T();
    }

    if(vector.size > capacity && array) {
        MemoryManager::free(array);
        array = (T*)MemoryManager::alloc(vector.size * sizeof(T));
        capacity = vector.size;
    }

    for (U64 i = 0; i < vector.size; ++i) {
        array[i] = vector.array[i];
    }

    size = vector.size;

    return *this;
}

template<class T>
Vector<T>::~Vector(void) {
    for (U64 i = 0; i < size; ++i) {
        array[i].~T();
    }

    if(array) {
        MemoryManager::free((void*)array);
        array = NULL;
    }
}

template<class T>
typename Vector<T>::VectorIterator Vector<T>::begin(void) const {
    VectorIterator it = {0};
    if(size == 0) {
        return it;
    }

    it.basePointer = const_cast<Vector<T>*>(this);
    it.currentPointer = const_cast<T*>(array);

    return it;
}

template<class T>
typename Vector<T>::VectorIterator Vector<T>::rBegin(void) const {
    VectorIterator it = {0};
    if(size == 0) {
        return it;
    }

    it.basePointer = const_cast<Vector<T>*>(this);
    it.currentPointer = const_cast<T*>(array + (size - 1));

    return it;
}

template<class T>
typename Vector<T>::VectorIterator Vector<T>::end(void) const {
    VectorIterator it = {0};
    if(size == 0) {
        return it;
    }

    it.basePointer = const_cast<Vector<T>*>(this);
    it.currentPointer = const_cast<T*>(array + size);

    return it;
}

template<class T>
typename Vector<T>::VectorIterator Vector<T>::rEnd(void) const {
    VectorIterator it = {0};
    if(size == 0) {
        return it;
    }

    it.basePointer = const_cast<Vector<T>*>(this);
    it.currentPointer = const_cast<T*>(array - 1);

    return it;
}

template<class T>
T& Vector<T>::front(void) const {
    if(size == 0) {
        defaultLog.log(CEngine::Logging::CONTAINERS, LOG_ERROR, "Accessed element in empty container!\n");
    }

    return array;
}

template<class T>
T& Vector<T>::back(void) const {
    if(size == 0) {
        defaultLog.log(CEngine::Logging::CONTAINERS, LOG_ERROR, "Accessed element in empty container!\n");
    }

    return array[size - 1];
}

template<class T>
void Vector<T>::pushBack(const T& element) {
    if(size == capacity) {
        if(array) {
            array = (T*)MemoryManager::grow(array, capacity * sizeof(T), capacity * sizeof(T) * 2);
            capacity *= 2;
        }
        else {
            array = (T*)MemoryManager::rawAlloc(sizeof(T) * 2);
            capacity = 2;
        }
    }

    array[size++] = element;
}

template<class T>
void Vector<T>::popBack(void) {
    if(size == 0) {
        return;
    }
    --size;
    array[size].~T();
}

template<class T>
void Vector<T>::insert(const Iterator<T>& position, const T& element) {
    if(size == capacity) {
        if(array) {
            array = (T*)MemoryManager::grow(array, capacity * sizeof(T), capacity * sizeof(T) * 2);
            capacity *= 2;
        }
        else {
            array = (T*)MemoryManager::rawAlloc(sizeof(T) * 2);
            capacity = 2;
        }
    }

    VectorIterator it;
    it.basePointer = this;
    it.currentPointer = array + (size - 1);

    if(position >= it) {
        array[size++] = element;
        return;
    }

    ++size;
    memmove(position + 1, position, (size - (U64)((position) - array)) * sizeof(T));

    array[position] = element;
}

template<class T>
void Vector<T>::insert(const Iterator<T>& position, const U64 n, const T& element) {
    if(size + n > capacity) {
        if(array) {
            U64 oldCapacity = capacity;
            for (; capacity < size + n;) {
                capacity *= 2;
            }

            array = (T*)MemoryManager::grow(array, oldCapacity * sizeof(T), capacity * sizeof(T));
        }
        else {
            capacity = 2;
            for (; capacity < size + n;) {
                capacity *= 2;
            }

            array = (T*)MemoryManager::rawAlloc(sizeof(T) * capacity);
        }
    }

    VectorIterator it;
    it.basePointer = this;
    it.currentPointer = array + (size - 1);

    if(position >= it) {
        for (U64 i = 0; i < n; ++i) {
            array[size++] = element;
        }
        return;
    }

    memmove(position + n, position, (size - (U64)((position) - array)) * sizeof(T));
    size += n;

    for (U64 i = 0; i < n; ++i) {
        array[position + i] = element;
    }
}

template<class T>
void Vector<T>::erase(const Iterator<T>& position) {
    if(size == 0) {
        return;
    }

    VectorIterator it;
    it.basePointer = this;
    it.currentPointer = array + (size - 1);

    if(position >= it) {
        array[--size].~T();
        return;
    }

    (*position).~T();
    memmove(position, position + 1, (size - (U64)((position) - array)) * sizeof(T));

    --size;
}

template<class T>
void Vector<T>::erase(const Iterator<T>& start, const Iterator<T>& e) {
    Iterator<T> end = e;
    if(size == 0 || start > end) {
        return;
    }
    if(end >= size) {
        end = array + (size - 1);
    }

    for (Iterator<T> it = start; it <= end; ++it) {
        (*it).~T();
    }

    memmove(start, end + 1, (size - (U64)(((end) - array) + 1)) * sizeof(T));
    size -= (U64)(((end) - array) - ((start) - array));
}

template<class T>
void Vector<T>::resize(const U64 n) {
    if(n <= size) {
        for (U64 i = size - 1; i >= n; --i) {
            array[i].~T();
        }

        size = n;
        return;
    }

    if(n > capacity) {
        if(array) {
            array = (T*)MemoryManager::grow(array, capacity * sizeof(T), n * sizeof(T));
        }
        else {
            array = (T*)MemoryManager::rawAlloc(n * sizeof(T));
        }
        capacity = n;
    }

    for (U64 i = size; i < n; ++i) {
        new(&array[i]) T;
    }

    size = n;
}

template<class T>
void Vector<T>::resize(const U64 n, const T& element) {
    if(n <= size) {
        for (U64 i = size - 1; i >= n; --i) {
            array[i].~T();
        }

        size = n;
        return;
    }

    if(n > capacity) {
        if(array) {
            array = (T*)MemoryManager::grow(array, capacity * sizeof(T), n * sizeof(T));
        }
        else {
            array = (T*)MemoryManager::rawAlloc(n * sizeof(T));
        }
        capacity = n;
    }

    for (U64 i = size; i < n; ++i) {
        array[i] = element;
    }
    size = n;
}

template<class T>
void Vector<T>::reserve(const U64 n) {
    if(n > capacity){
        if(array) {
            array = (T*)MemoryManager::grow(array, capacity * sizeof(T), n * sizeof(T));
        }
        else {
            array = (T*)MemoryManager::rawAlloc(n * sizeof(T));
        }
        capacity = n;
    }
}

template<class T>
void Vector<T>::shrink(const U64 n) {
    if(n < capacity){
        for (U64 i = size - 1; i != (n - 1); --i) {
            array[i].~T();
        }

        if(array) {
            T* temp = array;
            array = (T*)MemoryManager::rawAlloc(n * sizeof(T));
            memcpy(array, temp, n * sizeof(T));
        }
        else {
            array = (T*)MemoryManager::rawAlloc(n * sizeof(T));
        }
        capacity = n;
        if(size > capacity) {
            size = capacity;
        }
    }
}

template<class T>
void Vector<T>::clear(void) {
    for (U64 i = 0; i < size; ++i) {
        array[i].~T();
    }

    size = 0;
}

template<class T> inline
bool Vector<T>::isEmpty(void) const {
    if(size == 0) {
        return true;
    }
    return false;
}

template<class T> inline
U64 Vector<T>::getSize(void) const {
    return size;
}

template<class T> inline
U64 Vector<T>::getCapacity(void) const {
    return capacity;
}

template<class T> inline
T* Vector<T>::getRawArray(void) const {
    return array;
}

}

#endif // VECTORCORE_H_INCLUDED
